/**
 * Description: 文件服务器， 服务端
 * File: server.c |Time: 2021-04-13 17:21
 */

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <unistd.h>
#include <string.h>
#include <strings.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define BUF_SIZE    512 
#define OP_LIST     '1'
#define OP_DOWNLD   '2'
#define OP_UPLD     '3'
#define OP_QUIT     '4'

#define ERR(msg) do {   \
    perror(msg);        \
    printf("at: %s:%s%d\n", __FILE__, __func__, __LINE__); \
    exit(EXIT_FAILURE); \
} while(0)

int list(int fd);
int download(int fd);
int upload(int fd);

// 定义结构体发送数据包, 固定每次发送数据的大小
typedef struct {
    int len;
    char data[BUF_SIZE];
} pack;

int main(int argc, char *argv[])
{
    if (argc < 3){
        printf("Usage: %s <ip> <port>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd) {
        ERR("socket");
    }
    
    int clientfd;
    char user_option;
    char buf[BUF_SIZE] = {0};
    struct sockaddr_in serveraddr, clientaddr;
    socklen_t addrlen = sizeof(clientaddr);

    serveraddr.sin_family = AF_INET;
    serveraddr.sin_port = htons(atoi(argv[2]));
    serveraddr.sin_addr.s_addr = inet_addr(argv[1]);

    if (-1 == bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(struct sockaddr)))
    {
        ERR("bind");
    }

    if (-1 == listen(sockfd, 5))
    {
        ERR("listen");
    }

    printf("server %s:%s ready.\n", 
            inet_ntoa(serveraddr.sin_addr), argv[2]);

    clientfd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen);
    if (-1 == clientfd) {
        ERR("accept");
    }

    printf("%s:%d is connected.\n", 
            inet_ntoa(clientaddr.sin_addr), 
            ntohs(clientaddr.sin_port));

    // 开始通信
    while (1)
    {
        if (-1 == recv(clientfd, buf, BUF_SIZE, 0)) {
            ERR("recv");
        }

        user_option = buf[0];

        switch (user_option) {
            case OP_LIST:
                list(clientfd);
                break;
            case OP_DOWNLD:
                download(clientfd);
                break;
            case OP_UPLD:
                upload(clientfd);
                break;
            case OP_QUIT:
                printf("%s:%d is offline.\n", 
                    inet_ntoa(clientaddr.sin_addr), 
                    ntohs(clientaddr.sin_port));
                close(clientfd);
                exit(EXIT_SUCCESS);
                break;
            default:
                bzero(buf, BUF_SIZE);
                sprintf(buf, "不支持此操作: %d\n", user_option);
                send(sockfd, buf, sizeof(buf), 0);
                break;
        }
    }


    if (-1 == close(sockfd))
    {
        ERR("close");
    }
    return 0;
}


int list(int fd)
{
    printf("transfer file list.\n");
    DIR *dir_p = opendir(".");
    char buf[BUF_SIZE] = {0};
    struct dirent *dir_item;
    if (NULL == dir_p) {
        ERR("opendir");
    }
    
    while (1)
    {
        dir_item = readdir(dir_p);
        if (NULL == dir_item) {
            break;
        }

        if (strncmp(".", dir_item->d_name, 1) == 0) {
            continue;
        }

        bzero(buf, BUF_SIZE);

        if (-1 == send(fd, dir_item->d_name, BUF_SIZE, 0))
        {
            ERR("send");
        }
    }

    closedir(dir_p);
    printf("end transfer.\n");

    return 0;
}


int download(int fd)
{
    char buf[BUF_SIZE] = {0};
    int filefd = 0;
    int read_size = 0;

    if (-1 == recv(fd, buf, BUF_SIZE, 0)) {
        ERR("recv");
    }

    if (-1 == access(buf, F_OK)) {
        send(fd, "no", 2, 0);
        printf("file not found: %s\n", buf);
        return -1;
    }

    filefd = open(buf, O_RDONLY);
    if (-1 == filefd) {
        ERR("open");
    }

    printf("start transfer data.\n");
    bzero(buf, BUF_SIZE);

    while (1)
    {
        if (-1 == (read_size = read(filefd, buf, BUF_SIZE))) 
        {
            ERR("read");
        }

        send(fd, buf, read_size, 0);

        if (read_size < BUF_SIZE) {
            break;
        }
    }

    printf("end transfer.\n");
    return 0;
}

int upload(int fd)
{    
    char buf[BUF_SIZE] = {0};

    int recv_size = 0;
    int filefd = 0;

    if (-1 == recv(fd, buf, BUF_SIZE, 0)) {
        ERR("recv");
    }

    filefd = open(buf, O_CREAT | O_TRUNC | O_WRONLY, 0666);
    if (-1 == filefd) {
        printf("filename: %s\n", buf);
        ERR("open");
    }

    printf("begin transfer.\n");

    while(1)
    {
        bzero(buf, BUF_SIZE);
        recv_size = recv(fd, buf, BUF_SIZE, 0);
        

        printf("receive size: %d\n", recv_size);

        write(filefd, buf, recv_size);

        if (recv_size < BUF_SIZE) {
            break;
        }
    }

    if (close(filefd) == -1)
    {
        ERR("close");
    }

    printf("upload finish.\n");

    return 0;
}

